﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using MaxLeaf.KH.Contract;
using System.ServiceModel;
using System.ServiceModel.Channels;
using MaxLeaf.KH.DataContract;
using MaxLeaf.Commons;
using CommonsLib.Utility;
using CommonsLib.Settings;

namespace MaxLeaf.KH.DALProxy
{
    class BasicMtnDALWCFClient :BaseDALWCFClient, IBasicMtn
    {

        private   readonly List<WCFConfigInfo>  _lstChannels = null;
        public BasicMtnDALWCFClient(List<WCFConfigInfo> lst)
        {
            _lstChannels=lst;
        }

        #region 初始化 CHANNEL

        public static object _channelLock = "lock";
        public static List<int> _retries;
        public static List<ChannelFactory<IBasicMtn>> _factory;
        public static List<IBasicMtn> _channel;
        public static List<EndpointAddress> _endPointAddresses;
        public static List<string> _bindings;
        public static int serverCount = 0;

        public void initializeChannelList(List<WCFConfigInfo> serviceHostNodes, string action)
        {
            lock (_channelLock)
            {
                switch (action)
                {
                    case Const.WCF_INIT_ALL_CONNECTED_INSTANCES:
                        {
                            LogManager.Debug("Initializing channel list, checking " + serviceHostNodes.Count.ToString() + " from repository connection instance list.");
                            _endPointAddresses = new List<EndpointAddress>();
                            _bindings = new List<string>();
                            _retries = new List<int>();
                            _factory = new List<ChannelFactory<IBasicMtn>>();
                            _channel = new List<IBasicMtn>();
                            initAllConnectionInstances(serviceHostNodes);
                            break;
                        }

                    case Const.WCF_ADD_CONNECTED_SERVICE:
                        {
                            if (_endPointAddresses == null)
                                goto case Const.WCF_INIT_ALL_CONNECTED_INSTANCES;
                            for (int serverIndex = 0; serverIndex < serviceHostNodes.Count; serverIndex++)
                            {
                                EndpointAddress newEp = new EndpointAddress(serviceHostNodes[serverIndex].RemoteAddress);
                                EndpointAddress ep = _endPointAddresses.Find(delegate(EndpointAddress epExisting) { return epExisting.Uri.AbsoluteUri.Equals(newEp.Uri.AbsoluteUri); });
                                if (ep == null)
                                {
                                    LogManager.Debug("Adding Server " + serviceHostNodes[serverIndex].RemoteAddress + " to local load balanced list.");
                                    string bindingConfiguration = serviceHostNodes[serverIndex].BindingConfiguration;
                                    _endPointAddresses.Add(newEp);
                                    _bindings.Add(bindingConfiguration);
                                    _retries.Add(0);
                                    ChannelFactory<IBasicMtn> newFactory = new ChannelFactory<IBasicMtn>(bindingConfiguration, newEp);
                                    _factory.Add(newFactory);
                                    _channel.Add(newFactory.CreateChannel());
                                    ((IChannel)_channel[_channel.Count - 1]).Open();
                                }
                            }
                            serverCount = _channel.Count;
                            break;
                        }

                    case Const.WCF_REMOVE_CONNECTED_SERVICE:
                        {
                            if (_endPointAddresses == null || _endPointAddresses.Count == 0)
                                break;
                            for (int serverIndex = 0; serverIndex < serviceHostNodes.Count; serverIndex++)
                            {
                                EndpointAddress oldEP = new EndpointAddress(serviceHostNodes[serverIndex].RemoteAddress);
                                int index = _endPointAddresses.FindIndex(delegate(EndpointAddress epExisting) { return epExisting.Uri.AbsoluteUri.Equals(oldEP.Uri.AbsoluteUri); });
                                if (index >= 0)
                                {
                                    LogManager.Debug("Removing Server " + _endPointAddresses[index].Uri.AbsoluteUri.ToString() + " from local load balanced list.");
                                    try
                                    {
                                        if (_channel[index] != null)
                                            ((IChannel)_channel[index]).Abort();
                                        _channel[index] = null;
                                        if (_factory[index] != null)
                                            _factory[index].Abort();
                                        _factory[index] = null;
                                    }
                                    catch (Exception e)
                                    {
                                        LogManager.Error(e);
                                    }
                                    _factory.RemoveAt(index);
                                    _channel.RemoveAt(index);
                                    _retries.RemoveAt(index);
                                    _endPointAddresses.RemoveAt(index);
                                    _bindings.RemoveAt(index);
                                    serverCount--;
                                }
                            }
                            break;
                        }
                }
            }
        }

        private static void initAllConnectionInstances(List<WCFConfigInfo> serviceHostNodes)
        {
            if (serviceHostNodes == null || serviceHostNodes.Count == 0)
            {
                serverCount = 0;
                return;
            }
            List<int> retries = new List<int>();
            List<ChannelFactory<IBasicMtn>> channelFactory = new List<ChannelFactory<IBasicMtn>>();
            List<IBasicMtn> channel = new List<IBasicMtn>();
            List<EndpointAddress> endpoints = new List<EndpointAddress>();
            List<string> bindings = new List<string>();
            for (int serverIndex = 0; serverIndex < serviceHostNodes.Count; serverIndex++)
            {
                EndpointAddress remoteNodeEndPointAddress = new EndpointAddress(serviceHostNodes[serverIndex].RemoteAddress);
                string bindingConfiguration = serviceHostNodes[serverIndex].BindingConfiguration;
                endpoints.Add(remoteNodeEndPointAddress);
                bindings.Add(bindingConfiguration);
                retries.Add(0);
                channelFactory.Add(new ChannelFactory<IBasicMtn>(bindingConfiguration, remoteNodeEndPointAddress));
                channel.Add(channelFactory[serverIndex].CreateChannel());
            }
            for (int serverIndex = 0; serverIndex < serviceHostNodes.Count; serverIndex++)
            {
                try
                {
                    LogManager.Debug("Checking: " + serviceHostNodes[serverIndex].RemoteAddress + ". Binding Config: " + serviceHostNodes[serverIndex].BindingConfiguration);
                    if (((IChannel)channel[serverIndex]).State != CommunicationState.Opened)
                    {
                        ((IChannel)channel[serverIndex]).Open();
                    }

                    _channel.Add(channel[serverIndex]);
                    _bindings.Add(bindings[serverIndex]);
                    _endPointAddresses.Add(endpoints[serverIndex]);
                    _factory.Add(channelFactory[serverIndex]);
                    _retries.Add(retries[serverIndex]);
                    LogManager.Debug("Success!");
                }
                catch (Exception e)
                {
                    LogManager.Debug("Fail! Exception: " + e.Message + "");
                    if (((IChannel)channel[serverIndex]).State != CommunicationState.Opened)
                        ((IChannel)channel[serverIndex]).Abort();
                    if (channelFactory[serverIndex].State != CommunicationState.Opened)
                        channelFactory[serverIndex].Abort();
                }
            }
            serverCount = _channel.Count;
            LogManager.Debug("Final load-balanced server count is: " + serverCount + "");
            return;
        }

        public void InitCommunicationChannels(List<WCFConfigInfo> epList, string action)
        {
            //First, if its an empty list we will assume lets init all.  This goes straight to the ConnectedServiceInstances
            //table to get all connected primary and generic endpoints for possible initialization as channels.
            if (epList == null || epList.Count == 0 || action.Equals(Const.WCF_INIT_ALL_CONNECTED_INSTANCES))
            {

                epList = initChannels();
            }
            List<WCFConfigInfo> initClientList = new List<WCFConfigInfo>();

            foreach (WCFConfigInfo ep in epList)
            {

                initClientList.Add(ep);

            }
            initializeChannelList(initClientList, action);


            return;
        }

        private List<WCFConfigInfo> initChannels()
        {
            return _lstChannels;
        }


        #endregion

        #region 定义Channel

        private static Random rand = new Random(System.DateTime.Now.Millisecond);
        private int i = rand.Next(serverCount);
        private string server = null;

        public IBasicMtn Channel
        {
            get
            {
                if (serverCount == 0 || _channel == null || _channel.Count == 0)
                {
                    //This condition means we have no detectable, reachable service host instances.
                    lock (_channelLock)
                    {
                        if (serverCount == 0)
                        {
                            InitCommunicationChannels(null, Const.WCF_INIT_ALL_CONNECTED_INSTANCES);
                            if (_channel.Count == 0)
                                throw new Exception(Const.EXCEPTION_WCF_NOTREACHABLE);
                        }
                    }
                }
                int retrycyclecount = 0;
                while (serverCount > 0)
                {
                    int startCount = i;
                    if (_channel.Count < 1)
                        throw new Exception(Const.EXCEPTION_WCF_NOTREACHABLE);
                    IBasicMtn localChannel = _channel[i];
                    server = _endPointAddresses[i].Uri.AbsoluteUri;
                    if (localChannel == null)
                    {
                        int tmpserverCount = serverCount;
                        lock (_channelLock)
                        {
                            if (tmpserverCount == serverCount && i < _factory.Count)
                            {
                                if (_factory[i] == null)
                                {
                                    _factory[i] = new ChannelFactory<IBasicMtn>(_bindings[i], _endPointAddresses[i]);
                                }
                                if (_channel[i] == null)
                                {
                                    _channel[i] = _factory[i].CreateChannel();
                                }
                            }
                            localChannel = _channel[i];
                            server = _endPointAddresses[i].Uri.AbsoluteUri;
                        }
                    }

                    //No need for an online check if we are only operating against a single server.
                    //Online check on every call is expensive; but the benefit is immediate failover to
                    //other service nodes without missing a request.  But if only one server is in the
                    //cluster, there is no server to failover to anyway, so we can skip the check.
                    if (serverCount == 1)
                        return localChannel;
                    try
                    {
                        _retries[i] = 0;
                        return localChannel;
                    }
                    catch (Exception e)
                    {
                        lock (_channelLock)
                        {
                            if (i < _endPointAddresses.Count)
                            {
                                if (server.Equals(_endPointAddresses[i].Uri.AbsoluteUri))
                                {
                                    retrycyclecount++;
                                    _retries[i]++;
                                    if (_channel[i] != null && ((IChannel)_channel[i]).State != CommunicationState.Opened)
                                    {
                                        ((IChannel)_channel[i]).Abort();
                                        _channel[i] = null;
                                        if (_factory[i] != null && _factory[i].State != CommunicationState.Opened)
                                            _factory[i].Abort();
                                        _factory[i] = null;
                                    }
                                    if (retrycyclecount > Const.RETRY_CYCLES)
                                    {
                                        LogManager.Debug("Exceeded Retry Cycles: Throwing Exception back to Client.");
                                        //throw new Exception(e.Message);
                                    }
                                    if (_retries[i] > Const.BAD_REQUEST_LIMIT)
                                    {
                                        int tmpserverCount = serverCount;
                                        lock (_channel)
                                        {
                                            if (tmpserverCount == serverCount && tmpserverCount > 0)
                                            {
                                                try
                                                {
                                                    LogManager.Debug("Exceeded Bad Request Limit for: " + _endPointAddresses[i].Uri.AbsoluteUri + ". Removing host from load-balanced list.");
                                                }
                                                catch
                                                {
                                                }
                                                List<WCFConfigInfo> removeList = new List<WCFConfigInfo>();
                                                WCFConfigInfo removeInstance = new WCFConfigInfo();
                                                removeInstance.RemoteAddress = _endPointAddresses[i].Uri.AbsoluteUri;
                                                removeList.Add(removeInstance);
                                                initializeChannelList(removeList, Const.WCF_REMOVE_CONNECTED_SERVICE);
                                            }
                                        }
                                    }
                                    else
                                        i++;
                                }
                                else
                                    i++;
                            }
                            else
                                i = 0;
                        }
                        if (i > (serverCount - 1))
                            i = 0;
                    }
                }
                throw new Exception(Const.EXCEPTION_WCF_NOTREACHABLE);
            }

            set
            {
                //Nothing to do but throw the friendly exception since there are no servers online to process
                //the request at this moment.
                if (_channel.Count == 0 || !server.Equals(_endPointAddresses[i].Uri.AbsoluteUri))
                    return;
                lock (_channelLock)
                {
                    try
                    {
                        if (i >= _channel.Count)
                            return;
                        if (_channel[i] != null && ((IChannel)_channel[i]).State != CommunicationState.Opened)
                        {
                            if (_channel[i] != null)
                                ((IChannel)_channel[i]).Abort();
                            _channel[i] = null;
                            if (_factory[i] != null && _factory[i].State != CommunicationState.Opened)
                                if (_factory[i] != null)
                                {
                                    _factory[i].Abort();
                                    _factory[i] = null;
                                }
                        }
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Setter exception is: " + e.Message);
                    }
                    finally
                    {
                    }
                }
            }
        }
        #endregion

        public object Select(object model, int intKeyValue)
        {
            try
            {
                 
                 
                    return this.Channel.Select(model, intKeyValue);
                 
                 
            }
            catch
            {
                this.Channel = null;
                throw;
            }
        }


        public List<object>  SelectDtl(object model, int intForeignKeyValue)
        {
            try
            {


                return this.Channel.SelectDtl(model, intForeignKeyValue);


            }
            catch
            {
                this.Channel = null;
                throw;
            }
        }

        public object Add(object model)
        {
            try
            {
                 
                    return this.Channel.Add(model);
                 
                 
            }
            catch
            {
                this.Channel = null;
                throw;
            }
        }

        public object Update(object model)
        {
            try
            {
                 
                    return this.Channel.Update(model);
                
                 
            }
            catch
            {
                this.Channel = null;
                throw;
            }
        }

        public object Delete(object model)
        {
            try
            {
                 
                    return this.Channel.Delete(model);
                 
                 
            }
            catch
            {
                this.Channel = null;
                throw;
            }
        }
    }
}
